package com.aaa.oauth.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.JdbcClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.sql.DataSource;

@Configuration
// 启用认证服务器
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    public PasswordEncoder passwordEncoder;

    @Autowired
    public UserConfig userConfig;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenStore redisTokenStore;

    @Autowired
    private DataSource dataSource;

    /**
     * AuthorizationServerEndpointsConfigurer
     * 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)。
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)// 支持password模式
                .userDetailsService(userConfig) // 设置用户验证服务
                .tokenStore(redisTokenStore) // 指定token的存储方式
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        // endpoints.tokenServices(defaultTokenServices());
    }

    @Primary
    @Bean
    public DefaultTokenServices defaultTokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(redisTokenStore);
        // 启用刷新token
        tokenServices.setSupportRefreshToken(true);
        // token有效期自定义设置，默认12小时
        tokenServices.setAccessTokenValiditySeconds(60 * 60 * 12);
        // refresh_token默认30天
        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
        return tokenServices;
    }

    /**
     * ClientDetailsServiceConfigurer：
     * 在认证中心备案客户端，支持谁来获取token
     * 用来配置客户端详情服务（ClientDetailsService），客户端详情信息在这里进行初始化，
     * 能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息
     * <p>
     * authorization_code：授权码类型
     * 1.授权码模式（authorization_code）
     * 2.简化模式（implicit）
     * 3.密码模式（resource owner password credentials）
     * 4.客户端模式（client_credentials）
     * 5.通过以上授权获得的刷新令牌来获取新的令牌（refresh_token）
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 保存在内存中
        /*clients.inMemory()
                .withClient("customer")
                // 密钥
                .secret(passwordEncoder.encode("123"))
                // 授权类型
                .authorizedGrantTypes("refresh_token", "authorization_code", "password","client_credentials")
                // token的有效期
                .accessTokenValiditySeconds(3600)
                // 用来限制客户端访问的权限，在换取的token的时候会带上scope参数，只有在scopes定义内的，才可以正常换取token
                .scopes("all","read","write","a")
                .and()
                .withClient("oauth")
                .secret(passwordEncoder.encode("123"))
                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                .accessTokenValiditySeconds(3600)
                .scopes("all");*/
        // 通过数据库获取访问对象
        JdbcClientDetailsServiceBuilder jcsb = clients.jdbc(dataSource);
        jcsb.passwordEncoder(passwordEncoder);
    }

    /**
     * AuthorizationServerSecurityConfigurer 用来配置令牌端点(Token Endpoint)的安全约束
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 允许客户端访问 OAuth2 授权接口，否则请求token会返回 401
        security.allowFormAuthenticationForClients();
        /**
         * 允许已授权用户访问 checkToken 接口和获取 token 接口
         * POST /oauth/authorize  授权码模式认证授权接口
         * GET/POST /oauth/token  获取 token 的接口
         * POST  /oauth/check_token  检查 token合法性接口
         */
        security.checkTokenAccess("isAuthenticated()");
        security.tokenKeyAccess("isAuthenticated()");
    }
}
